#include "stock_analyzer_2.h"
#include <iostream>

// 买入时的记录
struct buy_date{
    std::string date;// 买入时间
    double price;// 买入价格
    size_t amount;// 买入的量 
};

struct stock_analyzer_2_data {
    // 一周内曾高位卖出
    int high_price_sell_out_in_one_week = 0;
    // 状态机上电平:
    bool inner_isLargeVolumeWashout = false;
    int count_large_volume_washout = 0;
    int count_continuous_large_volume_washout = 0;
    double buy_price = 0;    
    // 判断连涨三天
    bool was_up_3_days = false;
    // 模拟持仓
    size_t simulate_hold_stock = 0;
    // 模拟持仓价格
    double simulate_hold_stock_price = 0;
    // 统计买卖的次数, 计算卖出后的胜率
    size_t count_buy=0;
    size_t count_win=0;
    double total_profit_rate = 0;
    double total_loss_rate = 0;
    double total_earn = 0;
    double total_loss = 0;
    // 单只股票的买入栈
    std::vector<buy_date> buy_stack;
};

// 定义宏，将m_inner_data转换为stock_analyzer_2_data对象指针
#define GET_DATA() ((stock_analyzer_2_data*)m_inner_data)

// 一手股票的数量
const int LOT_SIZE = 100;

// 买多少手? (一手100只), 用于在跌的时候买更多
const int g_buyLots = 1;

stock_analyzer_2::stock_analyzer_2() {
    m_inner_data = (size_t)new stock_analyzer_2_data;
}


#include <chrono>
#include <sstream>
// 比较两个日期字符串大小的函数
bool compare_dates(const std::string& date_str1, const std::string& date_str2) {
    std::istringstream iss1(date_str1);
    std::istringstream iss2(date_str2);
    std::chrono::year_month_day ymd1;
    std::chrono::year_month_day ymd2;
    iss1 >> std::chrono::parse("%Y-%m-%d", ymd1);
    iss2 >> std::chrono::parse("%Y-%m-%d", ymd2);

    if (!iss1.fail() && !iss2.fail()) {
        return ymd1 < ymd2;
    }
    return false;
}


buy_or_sell stock_analyzer_2::adjustPosition(const stock_analyzer_data* data) {
    buy_or_sell result;

    if(0==data || 0==data->macd)
    {
        // 数据不完整, 可能是到了最后一天.
        return result;
    }
    // 没有买卖的情况下, 也为上层调用返回现价
    result.price = data->currentPrice;

    //std::cout<<"####    adjustPosition"<<std::endl;

    double ma5_slope = (data->ma5 - data->lastMa5)/data->currentPrice;
    double ma10_slope = (data->ma10 - data->lastMa10)/data->currentPrice;
    double ma20_slope = (data->ma20 - data->lastMa20)/data->currentPrice;
    //double middlePrice = (data->ma90DaysHeigh + data->ma90DaysLow)/2; // 这个判断谷底的方法不好, 有时就是一阳穿三线在半山腰往上冲

    



    if(data->isLargeVolumeWashout && false==GET_DATA()->inner_isLargeVolumeWashout)
    {
        GET_DATA()->inner_isLargeVolumeWashout = true;
        ++GET_DATA()->count_large_volume_washout;
        ++GET_DATA()->count_continuous_large_volume_washout;
    }
    else if(data->isLargeVolumeWashout)
    {
        ++GET_DATA()->count_large_volume_washout;
        ++GET_DATA()->count_continuous_large_volume_washout;
    }
    else if(!data->isLargeVolumeWashout && GET_DATA()->count_continuous_large_volume_washout>0)
    {
        if(GET_DATA()->count_continuous_large_volume_washout>2 && data->currentPrice>GET_DATA()->buy_price*1.05)
        {
            GET_DATA()->high_price_sell_out_in_one_week=14;
            GET_DATA()->was_up_3_days = false;
            GET_DATA()->inner_isLargeVolumeWashout = false;// 进一步防止高位买入
            GET_DATA()->count_large_volume_washout = 0;
        }
        GET_DATA()->count_continuous_large_volume_washout=0;
    }
    
    // 阻止短期内高位买入
    if(0<GET_DATA()->high_price_sell_out_in_one_week)
    {
        --GET_DATA()->high_price_sell_out_in_one_week;
        GET_DATA()->inner_isLargeVolumeWashout = false;
    }
    //std::cout<<"#65 找买入点:"<<"\t洗盘次数:"<<GET_DATA()->count_large_volume_washout<<"\tma5:"<<data->ma5<<"\tma10:"<<data->ma10<<"\tma20:"<<data->ma20<<"\tcurrentPrice:"<<data->currentPrice<<std::endl;
    // // 买入点:
    // if(data->ma5<data->ma20 && data->ma10<data->ma20 
    //     && data->currentPrice > data->ma5
    //     && data->currentPrice > data->ma10
    //     && data->currentPrice > data->ma20// 兼容一阳穿三线 删除一句就是一阳二穿线 但是, 放宽条件会造成胜率下降,之后没钱买入指数仓位
    //     && 3 < GET_DATA()->count_large_volume_washout // 有多次巨量洗盘的记录
    //     //&& data->isLargeVolumeWashout // 量放大 放量上涨
    //     //&& ma5_slope>0 // 追涨
    //     // && (
    //     //     compare_dates("2020-06-30",data->date)&&compare_dates(data->date, "2021-04-30") 
    //     //     || compare_dates("2023-01-31", data->date)&&compare_dates(data->date,"2023-08-31")
    //     //     || compare_dates("2024-03-29", data->date)
    //     //     )
    //     // && data->currentPrice>20 // 价格太低, 不买
    //     // && data->isKDJCross
    // )        
    // {
    //     // std::cout<<"#79 一阳穿三线"<<std::endl;
    //     // std::cout<<"均线斜率: "<<ma5_slope<<"\t"<<ma10_slope<<"\t"<<ma20_slope<<std::endl;// <<"用于比对的斜率:"<<(ma10_slope/params->currentPrice) 
    //     if( data->currentPrice > data->ma5
    //             && !(ma5_slope<0&&ma10_slope<0&&ma20_slope<0) // 不是在止损条件下
    //         )
    //     {
    //         // 计算该买入多少股
    //         int quantity = g_buyLots*LOT_SIZE;
    //         //buyCallback(quantity, data->currentPrice);
    //         if(!GET_DATA()->simulate_hold_stock)
    //         {
    //             result.buy=true;
    //             ++GET_DATA()->count_buy;
    //             result.price = data->currentPrice;
    //             GET_DATA()->simulate_hold_stock = true;
    //             GET_DATA()->simulate_hold_stock_price = data->currentPrice;
    //         }
    //     }
    // }

    // 试验买入点:
    if(//
        // data->isAfterOversold // 超卖后的一个普通叉
        // data->isMACDPositive        
        
        //data->macd>0
        //&& data->lastMacd<0 // 单单删除这句,胜率降很多
        // data->lastMacd<data->lastMacd_signal// 金叉 胜率不如上面的"零叉"
        // // && data->macd > data->macd_histogram
        // && data->macd > data->macd_signal // 金叉的上半部分 从.38的胜率, 做到.40
        // && data->lastMacd<data->lastMacd_signal
        // && data->lastMacd<data->macd_signal
        // && (data->macd-data->lastMacd)/data->currentPrice > 0.0002
        // && data->macd >0
        // && data->isKDJCross
        // && data->kdj_j > data->kdj_last_j // 似乎不影响胜率
        
        // && data->new_lows_and_bottoms_diverge
        //&& 
        data->isMACDGoUp

        // && data->has_depart_speed_up// 突发背离
        // && data->macd_depart > .3// 背离高
        //&& data->lastMacd<0
        //&& data->isOverbought
        // && data->ma5<data->ma20 && data->ma10<data->ma20 
        // && data->currentPrice > data->ma5
        // && data->currentPrice > data->ma10
        // && data->currentPrice > data->ma20
        // && (
        //     compare_dates("2020-06-30",data->date)&&compare_dates(data->date, "2021-04-30") 
        //     || compare_dates("2023-01-31", data->date)&&compare_dates(data->date,"2023-08-31")
        //     || compare_dates("2024-03-29", data->date)
        //     )
        // && data->currentPrice>20 // 价格太低, 不买
        
        // && data->isLargeVolumeWashout // 量放大 放量上涨 胜率加1%左右
        //&& data->vol > data->lastVol*4// 并不能代替上面那句, 本想平滑一些, 结果胜率降低很多. 从0.4降到0.28
                // && (
                //     compare_dates("2020-06-30",data->date)&&compare_dates(data->date, "2021-04-30") 
                //     || compare_dates("2023-01-31", data->date)&&compare_dates(data->date,"2023-08-31")
                //     || compare_dates("2024-03-29", data->date)
                //     )// 有这时间的限制, 反而更亏, 胜率降2%左右, 本来两年能赚变成亏的
                // && data->currentPrice>20 // 价格太低, 不买. 之前这句是能提高胜率, 现在却是降低胜率
    )
    {

        // 计算该买入多少股
        int quantity = g_buyLots*LOT_SIZE;
        // 首次买入
        if(!GET_DATA()->simulate_hold_stock
            // ma120:    
            && data->ma120 > data->currentPrice
            // 120日面积:
            && data->ma120Area.size()>=3
            // 判断不是大涨之后(这个不管用,21天还是太近了)
            && (data->ma120Area[0].second>0&&data->ma120Area[1].second>0&&data->ma120Area[2].second>0)
            // 判断不是在大跌:
            && data->ma120Area[0].second < data->ma120Area[1].second && data->ma120Area[1].second < data->ma120Area[2].second
            // 判断跌速减慢:
            && data->ma120Area[0].second - data->ma120Area[1].second > data->ma120Area[1].second-data->ma120Area[2].second
        )
        {
            // std::cout<<"买入时的macd:"<< data->macd<<"\t prev:"<< data->lastMacd<<"\t差值:"<< data->macd-data->lastMacd<<"\t差值与股价的比值:"<< (data->macd-data->lastMacd)/data->currentPrice <<std::endl;
            // std::cout<<"#207 买入时的面积:"<< data->ma120Area[0].second<<" < "<< data->ma120Area[1].second<<" < "<<data->ma120Area[2].second<<std::endl;
            result.buy=true;
            ++GET_DATA()->count_buy;
            result.price = data->currentPrice;
            result.arrange_value = data->macd_depart;//data->macd_depart;// data->arrange_value;
            GET_DATA()->simulate_hold_stock = 100;
            GET_DATA()->simulate_hold_stock_price = data->currentPrice;
            // 记录买入到栈:
            GET_DATA()->buy_stack.push_back({data->date, data->currentPrice,100});
            m_buy_and_sell.push_back(data->date);
            m_buy_or_sell.push_back(1);
        }
        else if(GET_DATA()->simulate_hold_stock!=0
             && data->currentPrice < GET_DATA()->buy_stack.back().price *.95)
        {
            result.buy=true;
            ++GET_DATA()->count_buy;
            result.price = data->currentPrice;
            result.arrange_value = data->macd_depart;//data->macd_depart;// data->arrange_value;
            GET_DATA()->simulate_hold_stock_price = data->currentPrice*100 + GET_DATA()->simulate_hold_stock*GET_DATA()->simulate_hold_stock_price;
            GET_DATA()->simulate_hold_stock += 100;
            GET_DATA()->simulate_hold_stock_price/=GET_DATA()->simulate_hold_stock;
            // 记录买入到栈:
            GET_DATA()->buy_stack.push_back({data->date, data->currentPrice,100});
            m_buy_and_sell.push_back(data->date);
            m_buy_or_sell.push_back(1);
        }
    }
    // END OF 买入点




    // if(GET_DATA()->simulate_hold_stock)
    // {
    //     std::cout<<"#181刚刚买入"<<std::endl;
    // }

    // std::cout<<"#86 找卖出点:"<<std::endl;
    // 卖出点:
    if(
        
        //data->ma5<data->ma10
        // MACD刚开始下落的时候
        !data->isMACDGoUp
        && data->macd>0
        // 判断是有一定的量:
        && data->vol > data->volMa60

        
        // && m_is_big_rise == true
        //!data->isMACDGoUp
        // || data->currentPrice<data->openPrice // 乌云盖顶
        // ||ma5_slope<0 
        //&& data->currentPrice < data->ma10 
        // || params->isLargeVolumeSellout // 庄家巨量出货 但是会提前卖出, 没吃到后面的大肉.
        )
    {
        // std::cout<<"#192 卖 macd:"<< data->macd<<"\t prev:"<< data->lastMacd <<std::endl;
        //sellCallback(100, data->currentPrice);
        // std::cout<<data->date<<"一级卖出点条件满足"<<"\t"<< data->currentPrice <<">" <<GET_DATA()->simulate_hold_stock_price <<"\tma120:"<<data->ma120 <<std::endl;

        // 最严格的一个条件给栈底
        if(GET_DATA()->simulate_hold_stock==100 
            && data->ma120 < data->currentPrice
            && data->currentPrice>GET_DATA()->simulate_hold_stock_price*1.1
        )
        {
            // std::cout<<data->date<<"最终卖出点条件满足"<<std::endl;
            result.sell = true;            
            result.price = data->currentPrice;
            if(data->currentPrice > GET_DATA()->simulate_hold_stock_price)
            {
                ++GET_DATA()->count_win;
                GET_DATA()->total_profit_rate +=data->currentPrice/GET_DATA()->simulate_hold_stock_price-1;
                GET_DATA()->count_large_volume_washout=0;
                GET_DATA()->total_earn+=(data->currentPrice-GET_DATA()->simulate_hold_stock_price)*GET_DATA()->simulate_hold_stock;
            }
            else
            {
                GET_DATA()->total_loss_rate+=1-data->currentPrice/GET_DATA()->simulate_hold_stock_price;
                GET_DATA()->total_loss+=(GET_DATA()->simulate_hold_stock_price-data->currentPrice)*GET_DATA()->simulate_hold_stock;
            }
            GET_DATA()->simulate_hold_stock = 0;
            GET_DATA()->buy_stack.pop_back();
            m_buy_and_sell.push_back(data->date);
            m_buy_or_sell.push_back(-1);
        }
        else if(GET_DATA()->buy_stack.size()>1)
        {
            // std::cout<<data->date<<"做T卖出点条件满足"<<std::endl;
            bool have_a_sell = false;
            // 卖出比现在的价格低的节点:
            for(size_t i=GET_DATA()->buy_stack.size()-1; i>0; --i)
            {
                const buy_date &buy_pos = GET_DATA()->buy_stack[i];
                if(buy_pos.price < data->currentPrice)
                {
                    result.sell = true;
                    result.price = data->currentPrice;
                    if(data->currentPrice > GET_DATA()->buy_stack[i].price)
                    {
                        ++GET_DATA()->count_win;
                        GET_DATA()->total_profit_rate +=data->currentPrice/GET_DATA()->buy_stack[i].price-1;
                    }
                    // 记录赚了多少
                    GET_DATA()->total_earn+=(data->currentPrice-buy_pos.price)*buy_pos.amount;
                    GET_DATA()->buy_stack.pop_back();
                    have_a_sell = true;
                    GET_DATA()->simulate_hold_stock-=buy_pos.amount;
                }
                else
                {
                    break;
                }
            }
            if(have_a_sell)
            {
                m_buy_and_sell.push_back(data->date);
                m_buy_or_sell.push_back(-1);
            }

        }
    }
    // END OF 卖出点

    // if(GET_DATA()->simulate_hold_stock)
    // {
    //     std::cout<<"调试为什么不卖?"<<"\tmacd:"<< data->macd<<"\tlast_macd:"<<data->lastMacd
    //         <<"\tisGoUp?"<< data->isMACDGoUp <<"\tma5_slope:"<<ma5_slope 
    //         <<"\tdate:"<<data->date
    //         <<std::endl;
    // }

    // 判断大涨:
    if(data->currentPrice > data->ma120*1.5)
    {
        // std::cout<<"#252大涨"<<std::endl;
        if(false == m_is_big_rise)
        {
            m_is_big_rise = true;
            // 记录大涨时间
            m_big_rise_time.push_back(data->date);
            // 记录特征:
            m_before_big_rise_trait_record.push_back(m_before_big_rise_trait);
        }
    }
    else if(m_is_big_rise&&data->currentPrice < data->ma120)
    {
        m_is_big_rise = false;
        // 大涨刚过, 重新计算
        m_before_big_rise_trait = before_big_rise_trait();
        m_before_big_rise_trait_record_state = before_big_rise_trait_record_state();
    }
    else
    {
        // BEGIN 大涨前的特征:
        if(data->currentPrice < data->ma120)
        {
            m_before_big_rise_trait.price_less_than_ma120 = true;
            m_before_big_rise_trait_record_state.price_less_than_ma120 = 0;
        }
        else
        {
            ++m_before_big_rise_trait_record_state.price_less_than_ma120;
            if(15<=m_before_big_rise_trait_record_state.price_less_than_ma120)
            {
                m_before_big_rise_trait.price_less_than_ma120 = false;
            }
        }
        if(data->macd_activity_rate<1 && m_before_big_rise_trait_record_state.last_macd_activity_rate < data->macd_activity_rate)
        {
            m_before_big_rise_trait.macd_depart_speed_up_and_close_to_one = true;
            m_before_big_rise_trait_record_state.macd_depart_speed_up_and_close_to_one = 0;
        }
        else
        {
            ++m_before_big_rise_trait_record_state.macd_depart_speed_up_and_close_to_one;
            if(15<=m_before_big_rise_trait_record_state.macd_depart_speed_up_and_close_to_one)
            {
                m_before_big_rise_trait.macd_depart_speed_up_and_close_to_one = false;
            }
        }
        if(data->vol < data->lastVol && data->currentPrice<data->lastDayPrice)
        {
            m_before_big_rise_trait.has_small_volume_sellout = true;
            m_before_big_rise_trait_record_state.has_small_volume_sellout = 0;
        }
        else
        {
            ++m_before_big_rise_trait_record_state.has_small_volume_sellout;
            if(15<=m_before_big_rise_trait_record_state.has_small_volume_sellout)
            {
                m_before_big_rise_trait.has_small_volume_sellout = false;
            }
        }
        if(data->macd_depart_consecutive_days_of_gains>0)
        {
            m_before_big_rise_trait.macd_depart_speed_up_days = data->macd_depart_consecutive_days_of_gains;
            m_before_big_rise_trait_record_state.macd_depart_speed_up_days=0;
        }
        else
        {
            ++m_before_big_rise_trait_record_state.macd_depart_speed_up_days;
            if(15<=m_before_big_rise_trait_record_state.macd_depart_speed_up_days)
            {
                m_before_big_rise_trait.macd_depart_speed_up_days = 0;
            }
        }
        if(data->macd_depart>m_before_big_rise_trait.max_macd_depart_value_in_15_days)
        {
            m_before_big_rise_trait.max_macd_depart_value_in_15_days = data->macd_depart;
            m_before_big_rise_trait_record_state.max_macd_depart_value_in_15_days=0;
        }
        else
        {
            ++m_before_big_rise_trait_record_state.max_macd_depart_value_in_15_days;
            if(15<=m_before_big_rise_trait_record_state.max_macd_depart_value_in_15_days)
            {
                m_before_big_rise_trait.max_macd_depart_value_in_15_days = data->macd_depart;
                m_before_big_rise_trait_record_state.max_macd_depart_value_in_15_days=0;
            }
        }
        m_before_big_rise_trait_record_state.last_macd_activity_rate = data->macd_activity_rate;
        // END 大涨前的特征
    }

    return result;
}

double stock_analyzer_2::getWinRate()
{
    return GET_DATA()->count_win*1.0/GET_DATA()->count_buy;
}

double stock_analyzer_2::getProfitRatio()
{
    return GET_DATA()->total_profit_rate;
}

double stock_analyzer_2::getLossRatio()
{
    return GET_DATA()->total_loss_rate;
}


/**
 * 取大涨点:
 */
const std::vector<std::string> & stock_analyzer_2::getBigRiseTime() const
{
        return m_big_rise_time;
}
const std::vector<before_big_rise_trait> &stock_analyzer_2::getBeforeBigRiseTraitRecord() const
{
    return m_before_big_rise_trait_record;
}
const double stock_analyzer_2::getProfit() const
{
    return GET_DATA()->total_earn - GET_DATA()->total_loss;
}
/**
 * 买卖点
 */
const std::vector<std::string> &stock_analyzer_2::getBuyAndSell() const
{
    return m_buy_and_sell;
}
/**
 * 卖入或卖出
 */
const std::vector<int> &stock_analyzer_2::getBuyOrSell() const
{
    return m_buy_or_sell;
}

/**
 * 一开始就持股,以能让算法计算卖出点
 */
void stock_analyzer_2::setStartHold()
{
    GET_DATA()->simulate_hold_stock = 100;
}

/**
 * 在初始化时,设置买入栈
 */
void stock_analyzer_2::setBuyStack(double price,size_t quantity)
{
    GET_DATA()->buy_stack.push_back({"",price,100});
}
